'use strict';
const crypto = require('crypto');
const path = require('path');
const fs = require('fs');

class DesCipher {
  /**
   * 创建译码对象
   * @param {String} key key
   * @param {String} iv iv
   */
  constructor(key, iv) {
    this.key = key;
    this.iv  = iv;
  }

  /**
  * 加密
  * @param {String} data 加密数据
  * @return {String} 加密后内容
  */
  encrypt(data) {
    const cipher = crypto.createCipheriv('des', this.key, this.iv);
    const clearEncoding = 'utf8';
    const cipherEncoding = 'base64';
    const cipherChunks = [];
    cipher.setAutoPadding(true);
    cipherChunks.push(cipher.update(data, clearEncoding, cipherEncoding));
    cipherChunks.push(cipher.final(cipherEncoding));
    return cipherChunks.join('');
  }

  /**
  * 解密
  * @param {String} data 待解密的字符串
  * @return {String} 加密后内容
  */
  decrypt(data) {
    const clearEncoding = 'utf8';
    const cipherEncoding = 'base64';
    const cipherChunks = [];
    this.cipher.setAutoPadding(true);
    cipherChunks.push(this.cipher.update(data, cipherEncoding, clearEncoding));
    cipherChunks.push(this.cipher.final(clearEncoding));
    return cipherChunks.join('');
  }
}

module.exports = {

  /**
   * 写入临时文件夹中
   * @param {ReadableStream} readableStream 读流
   * @param {String} fileExtension 路径
   * @param {String} event 监听某一事件作为返回
   * @return {Promise<{filePath: String, fileHash: String}>} filePath 文件路径, fileHash  文件md5值
   */
  writeToTempPlace(readableStream, fileExtension, event = 'close') {
    const md5sum = crypto.createHash('md5');
    const filePath = path.resolve(this.app.config.messagePublish.tempPlace, `${this.toTimeStamp()}${fileExtension}`);
    const writeStream = fs.createWriteStream(filePath);
    return new Promise((res, rej) => {
      readableStream.pipe(writeStream);
      readableStream.on('data', function(chunk) {
        md5sum.update(chunk);
      });
      writeStream.on(event, () => {
        res({
          filePath,
          fileHash: md5sum.digest('hex'),
        });
      });
      readableStream.on('error', err => {
        rej(err);
      });
      writeStream.on('error', err => {
        rej(err);
      });
    });
  },

  /**
   * 判断是否是自然数
   * @param {Number} num 数字
   * @param {Boolean} needPositive 是否需要是正整数
   * @return {Boolean} 是否是自然数
   */
  judgeNaturalNumber(num, needPositive = false) {
    return Number.isInteger(num) && num >= 0 && (!needPositive || num > 0);
  },

  DesCipher,
  /**
   * 获取时间戳
   * @param {Date} date 时间
   * @return {Integer} 时间戳
   */
  toTimeStamp(date = new Date()) {
    if (typeof date  === 'string') {
      date = new Date(date);
    }
    if (isNaN(date)) {
      date = new Date();
    }
    return parseInt(Number(date) / 1000, 10);
  },

  /**
   * 校验参数
   * @param {Object} rule 校验规则 参照parameter 包
   * @param {Object} value 校验参数 如ctx.request.body
   * @param {Method} extraRule 额外自定义规则
   示例1： 需要对参数进行额外校验
  ctx.helper.validate(rule1, ctx.query, (query) => {
     if(query.para1 !== '11') throw { msg: 'para1 不等于 11 ！' };
  })
   示例2: 需要对参数进行默认处理 注 result为关键字 不可添加
  const { page, size } = ctx.helper.validate(rule1, ctx.query, (query) => {
    return {
      page: +query.page,
      size: +query.size,
    }
  })
   * @return {Object} extraRule校验返回
   */
  validate(rule = {}, value, extraRule = () => {}) {
    const { ctx } = this;
    try {
      if (!value) {
        value = ctx.request.body;
      }
      ctx.validate(rule, value);
      const extraRes = extraRule(value);
      const res = {
        result: 0,
      };
      Object.assign(res, value);
      Object.assign(res, extraRes);
      return res;
    } catch (err) {
      ctx.logger.warn(`参数校验失败 err ${JSON.stringify(err)}`);
      let msg = '';
      if (err.msg) {
        msg = err.msg;
      } else if (err.errors) {
        msg = err.errors.map(error => `${error.field} ${error.message}`).join(' and ');
      } else {
        msg = `参数校验异常 ${JSON.stringify(err.stack ? err.stack : err)}`;
      }
      ctx.throw(422, msg);
    }
  },
};
